'/
' RabDB (Rabid DataBases) - An RDL library.
' Written 2011 by Anton Berlin.
' 
' =========================================================================
' 
'                                Version 1.0
' 
' =========================================================================
' 
' This program is free software; you can redistribute it and/or modify
' it under the terms of the GNU Lesser General Public License as published
' by the Free Software Foundation; either version 3 of the License.
' 
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
' GNU General Public License for more details.
' 
' You should have received a copy of the GNU Lesser General Public License
' along with this program; if not, write to the Free Software
' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
'/

superstrict

import "errormanager.bmx"
import "SourceFile.bmx"
import "Database.bmx"
import "Lexer.bmx"
import "Parser.bmx"
import "Symbol.bmx"

type RabDB

	'// -----------------------------------------------------------------------------------------------------------------
	'// Load a database to memory.
	'// -----------------------------------------------------------------------------------------------------------------
	function loadDatabase:Database(url:string, dumpTokenStream:byte = false, skipErrors:byte = false)
	
		local errors:TList = new TList;
		local srcFile:SourceFile = SourceFile.create(url, errors);
		local db:Database = null;
		local symb:Symbol = null;
		
		if (not srcFile ) ..
			return null;
		
		local lex:DBLexer = DBLexer.create(srcFile);
		lex.analyse(errors);
		
		if (dumpTokenStream) ..
			lex.dumpToDisc();
		
		if (errors.count() = 0)
		
			local par:DBParser = DBParser.create(lex);
			
			symb = par.parse(errors);
			
			if (symb) ..
				db = Database(convert(symb));
		
		end if
		
		if (errors.count() and not skipErrors)
		
			for local e:Error = eachin errors
			
				errormanager.writeError(e);
			
			next
			
			return null;
		
		end if
		
		return db;
	
	end function
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Save a database to disc.
	'// -----------------------------------------------------------------------------------------------------------------
	method saveDatabase:byte(db:Database, url:string)
	
		if (fileType(url) = FILETYPE_FILE) ..
			deleteFile(url);
		
		local filestream:TStream = writeStream(url);
		
		if (not filestream) then
		
			writestderr(stripAll(appFile) + ": Could not access disc when saving the database to disc.");
			return false;
		
		end if
		
		db.saveToDisc(filestream);
		filestream.close();
		
		return true;
	
	end method
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Prevent this type to be instantiated.
	'// -----------------------------------------------------------------------------------------------------------------
	method new()
	
		local id:string = TTypeId.forObject(self).Name();
		runtimeError("Illegal to create an instance of a namespace class '" + id + "'.");
	
	end method

end type

private

'// -----------------------------------------------------------------------------------------------------------------
'// Convert a symbol tree to a database tree.
'// -----------------------------------------------------------------------------------------------------------------
function convert:object(symb:Symbol)

	local databaseNode:Database = null;
	local tableNode:Table = null;
	local rowNode:Row = null;
	local columnNode:Column = null;
	local returnObj:object = null;
	
	select (symb.m_type)
	
		case Symbol.TRANSLATION_UNIT
			databaseNode = new Database;
			databaseNode.m_name = symb.m_name;
			databaseNode.pushDatabase(Database(convert(Symbol(symb.m_symbolList.first()))));
			returnObj = databaseNode;
		case Symbol.DATABASE
			databaseNode = new Database;
			databaseNode.m_name = symb.m_name;
			
			for local node:Symbol = eachin symb.m_symbolList
			
				if (node.m_type = Symbol.DATABASE) ..
					databaseNode.pushDatabase(Database(convert(node)));
				if (node.m_type = Symbol.TABLE) ..
					databaseNode.pushTable(Table(convert(node)));
			
			next
			
			returnObj = databaseNode;
		case Symbol.TABLE
			tableNode = new Table;
			tableNode.m_name = symb.m_name;
			
			for local node:Symbol = eachin symb.m_columnList
			
				tableNode.pushColumn(Column(convert(node)));
			
			next
			
			for local node:Symbol = eachin symb.m_symbolList
			
				rowNode = Row(convert(node));
				tableNode.pushRow(rowNode);
				
				for local col:Symbol = eachin node.m_symbolList
				
					tableNode.setString(rowNode.m_name, col.m_name, col.m_value);
				
				next
			
			next
			
			returnObj = tableNode;
		case Symbol.COLUMN
			columnNode = new Column;
			columnNode.m_name = symb.m_name;
			columnNode.m_datatype = symb.m_datatype;
			returnObj = columnNode;
		case Symbol.ROW
			rowNode = new Row;
			rowNode.m_name = symb.m_name;
			returnObj = rowNode;
	
	end select
	
	return returnObj;

end function
